#!/usr/bin/python
# -*- coding:utf-8 -*-
# @time: 2021/5/10 3:05 下午

import uuid
import traceback

from django.utils import timezone
from django.core.files.base import File
from django.conf import settings
from django.http.response import FileResponse
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from rest_framework.exceptions import ValidationError


from apps.account_manager.models import User
from apps.account_manager.views import MfaValidateMixin
from .serializer import (
    NetworkSerializer,
    RouteSerializer
)
from apps.target.serializer import TargetGroups, resave_target_groups
from apps.global_config.serializer import get_global_config
from apps.warn_center.serializer import FakeRequest

from wass.settings import DDISK_THRESHOLD
from apps.system_manage.display import Tips


# 邮件相关
from utilities.email.wass_email import WassEmailSender
from utilities.enums import EmailTemplate
from utilities.redis import captcha_redis as redis

# 日志中心
from utilities.log import audit_log
from utilities.enums import AuditLogAction, LogModuleName, LogTypeName
from utilities.djconsole import djconsole


class LicenseView(APIView):
    """
    产品信息
    get:
    查询当前设备证书信息
    post:
    上传证书, 文件参数file
    """

    permission_classes = [
        AllowAny,
    ]

    def get(self, request, *args, **kwargs):
        token = request.META.get("HTTP_AUTHORIZATION")
        if token:
            license_info = djconsole.get_license_info(request)
            update_info = djconsole.get_update_version(request)
            license_info.update(
                {
                    "version": update_info.get('current', ''),
                    'current': update_info.get('current', ''),
                    "data_version": update_info.get('data_version', ''),
                    'system_version': update_info.get('system_version', ''),
                    'poc_version': update_info.get('poc_version', ''),
                    'max_task_session_number': license_info.get(
                        'max_task_session_number', settings.DEFAULT_TASK_SESSION_NUMBER),
                    'max_vul_template_number': license_info.get(
                        'max_vul_template_number', settings.DEFAULT_VUL_TEMPLATE_NUMBER),
                    'custom_content': license_info.get('custom_content', {})
                }
            )
        else:
            license_info = djconsole.get_license_device(request)
        data = {"status": "success", "errcode": 0, "data": license_info}
        return Response(data=data)

    def create_super_user(self):
        all_user = User.objects.all()
        if not all_user:
            super_user = "admin"
            super_pw = uuid.uuid4().hex
            try:
                User.create_superuser(
                    username=super_user, password=super_pw, group=["管理员"]
                )
                User.create_user(
                    username="operator", password=uuid.uuid4().hex, group=["操作员"]
                )
                User.create_user(
                    username="auditor", password=uuid.uuid4().hex, group=["审计员"]
                )
            except Exception as e:
                raise ValidationError(str(e))
            else:
                return {"username": super_user, "password": super_pw}
        return

    def post(self, request, *args, **kwargs):
        product_file = request.data.get("file")
        if not isinstance(product_file, File):
            raise ValidationError("请上传正确的证书文件")
        djconsole.upload_license(request, product_file)
        data = {"status": "success", "errcode": 0, "message": "证书上传成功"}
        # 证书上传不对历史周期任务做调整 CM-3004
        # resave_target_groups(TargetGroups.objects.filter())
        get_global_config().update(task_session_count=0)

        # 增加日志
        log_data = {
            "module": LogModuleName.system_management.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.upload_ssl.value,
            "desc": {"detail": "上传产品证书"},
        }
        # self.broken_site()
        try:
            user_info = self.create_super_user()
        except ValidationError:
            data.update({"message": "证书上传成功, 请联系管理员初始化用户"})
            user_info = None
        if user_info:
            data.update({"data": user_info})
        audit_log(request=self.request, **log_data)
        return Response(data=data)


class SystemStatusView(APIView):
    """
    系统状态
    get:
    查询系统状态
    """

    def get(self, request, *args, **kwargs):
        qtype = request.query_params.get('qtype')
        if qtype not in ['system', 'business']:
            raise ValidationError("没有该负载")

        if qtype == 'system':
            data = djconsole.get_device_system_status(request)
        else:
            data = djconsole.get_device_business_status(request)

        data = {"status": "success", "errcode": 0, "data": data}
        return Response(data=data)


class DDiskUsageView(APIView):
    """
    数据磁盘占用率
    """

    permission_classes = [AllowAny, ]

    @staticmethod
    def send_fst_email(request, out_memery):
        """节点首次 超过限制 发送邮件"""
        email = getattr(User.objects.filter(is_superuser=True).first(), 'email', None)
        if not email:  # 无收件人
            return

        for node in out_memery:
            key = f'{DDISK_THRESHOLD}:{node["node_ip"]}'
            is_send = redis.getset(key, 1)  # 已经发过邮件 页面也有提示，只发送一次就行
            if is_send:
                continue

            title = ''
            content = f'节点{node["node_ip"]} '
            if node['sys_usage'] > DDISK_THRESHOLD:
                content += f'系统磁盘占用率： {node["sys_usage"]};'
                title += ' 系统磁盘'
            if node['data_usage'] > DDISK_THRESHOLD:
                content += f'数据磁盘占用率： {node["data_usage"]};'
                title += ' 数据磁盘'
            content += '为不影响正常服务，请及时处理。'
            title += '使用率磁盘告警'

            args = {
                'to': [email],
                'title': title,
                'body': {'%content%': content, '%email%': email, '%title%': title},
            }
            WassEmailSender().send(EmailTemplate.ddisk.value, args)

    def get(self, request, *args, **kwargs):
        """获取数据磁盘利用率
        todo 告警触发方案 临时方案 待沟通
        """
        out_memery = self.check_usage(request, self)

        data = {
            "status": "success",
            "errcode": 0,
            "data": {'out_memery': out_memery, 'threshold': DDISK_THRESHOLD},
        }
        return Response(data=data)

    @staticmethod
    def check_usage(request, cls):
        out_memery = []
        device_id_list = [djconsole.get_sys_device(FakeRequest())]
        for device_id in device_id_list:
            tail_data = djconsole.get_dvol_usage(
                FakeRequest(), device_id=device_id
            )
            device_data = {
                "node_ip": tail_data.get("node_ip", ""),
                "sys_usage": tail_data.get("disk_sys_percent", 0),
                "data_usage": tail_data.get("disk_data_percent", 0)
            }
            out_memery.append(device_data)
        if request.query_params.get('master'):
            master = [
                out for out in out_memery if out['node_ip'] in [djconsole.master_ip, "127.0.0.1"]
            ]
            out_memery = master[0] if master else {}
        else:  # 监测是否需要发送邮件
            try:
                cls.send_fst_email(request, out_memery)
            except Exception:  # 邮件发送不影响主功能
                traceback.print_exc()
        return out_memery


class SystemNtpView(APIView):
    """系统时间"""

    def get(self, request, *args, **kwargs):
        result = djconsole.get_ntp_info(request)
        return Response(data=result)

    def post(self, request, *args, **kwargs):
        action = self.request.data.get("action")
        service_status = self.request.data.get('service_status')
        server_ip = self.request.data.get('server_ip')
        server_times = self.request.data.get('server_times')
        post_date = {
            "service_status": service_status,
            "server_ip": server_ip,
            "server_times": server_times,
        }
        if action not in ['wass', 'lo', 'ntp']:
            raise ValidationError("没有该方法")
        if action == "wass":
            result = djconsole.set_ntp_conf(request, post_date)
            if service_status != 1:
                message = "关闭ntp服务"
            else:
                message = f"开启ntp服务，设置ntp服务器ip为：{server_ip}"
        elif action == "lo":
            result = djconsole.set_loc_time(request, post_date)
            message = f"设置本地时间为:{server_times}"
        elif action == "ntp":
            result = djconsole.set_ntp_time(request, post_date)
            message = f"与ntp服务器:{server_ip}同步时间"
        log_data = {
            "module": LogModuleName.system_management.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.ntp.value,
            "desc": {"detail": message},
        }
        audit_log(request=self.request, **log_data)
        return Response(data=result)


class UpdateView(APIView):
    """
    系统, 数据升级
    get:
    查询系统版本信息 或者 数据版本信息,
    查询参数tag, tag只能是['data', 'system']中的一个， data=数据升级， system=系统升级
    put:
    修改升级方式, form参数manual, 升级方式, 0自动升级， 1手动升级, tag, ['data', 'system']中的一个， data=数据升级， system=系统升级,
    post:
    升级系统或者数据, 文件参数update_file，  form参数tag, tag只能是['data', 'system']中的一个
    data=数据升级， system=系统升级
    """

    def get(self, request, *args, **kwargs):
        tag = self.request.query_params.get("tag")
        if tag not in ['data', 'system']:
            raise ValidationError("查询信息错误")
        data = {
            "status": "success",
            "errcode": 0,
            "data": djconsole.get_update_info(request, tag),
        }
        return Response(data=data)

    def check_license(self, request):
        license = djconsole.get_license_info(request)
        if license.get("product_status") != "有效":
            raise ValidationError("证书不存在或者证书已过期不能升级")

    def put(self, request, *args, **kwargs):
        license_status = self.check_license(request)
        if license_status:
            return license_status
        tag = request.data.get("tag")
        if tag not in ['data', 'system']:
            raise ValidationError("不支持的升级类型")
        manual = request.data.get("manual")
        if isinstance(manual, str) and not manual.isdigit():
            raise ValidationError("升级方式选择错误")
        manual = int(manual)
        if manual not in [0, 1]:
            raise ValidationError("升级方式选择错误")
        system_manual, data_manual = self.get_update_way(request, tag, manual)
        djconsole.update_update_config(request, {"manual_data": data_manual, "manual_system": system_manual})
        # 增加日志
        if tag == "data":
            desc_type = "数据升级"
        else:
            desc_type = "系统升级"
        log_data = {
            "domain": "",
            "property_name": "",
            "module": "system",
            "action": "up",
            "type": "{}_upgrade".format(tag),
            "desc": {"detail": "{}升级方式修改为{}".format(desc_type, "手动升级" if manual else "自动升级")}
        }
        audit_log(request=self.request, **log_data)
        data = {
            "status": "success",
            "errcode": 0,
            "message": "修改成功!"
        }
        return Response(data=data)

    def post(self, request, *args, **kwargs):
        license_status = self.check_license(request)
        if license_status:
            return license_status
        upgrade_type = request.data.get("upgrade_type")
        update_file = request.data.get("update_file")
        if not isinstance(update_file, File):
            raise ValidationError("请上传正确的升级文件")
        if upgrade_type not in ['data', 'system']:
            raise ValidationError("不支持的升级类型")
        last_info = djconsole.get_update_info(request, upgrade_type) or dict()
        djconsole.upload_and_upgrade(request, {"file": update_file, "upgrade_type": upgrade_type})
        # # 同时还要更新各个从节点
        # djconsole.upload_and_upgrade_clusters(
        #     request, {"file": update_file, "tag": tag}
        # )

        now_info = djconsole.get_update_info(request, upgrade_type) or dict()

        if upgrade_type == 'data':
            _change_type = '数据'
            _change_key = 'data_version'
        else:
            _change_type = '系统'
            _change_key = 'system_version'

        # 增加日志
        _change = f"{last_info.get(_change_key)}-->{now_info.get(_change_key)}"
        log_data = {
            "module": LogModuleName.system_management.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.system_upgrade.value,
            "desc": {
                "detail": f"升级{_change_type}, {_change}"
            },
        }
        audit_log(request=self.request, **log_data)
        data = {"status": "success", "errcode": 0, "message": Tips().get(2)}
        return Response(data=data)

    def get_update_way(self, request, tag, manual):
        data = djconsole.get_update_info(request, tag)
        system_manual = data.get('manual_system', True)
        data_manual = data.get('manual_data', True)
        if tag == 'data' and manual == 0:
            data_manual = False
        if tag == 'data' and manual == 1:
            data_manual = True
        if tag == 'system' and manual == 0:
            system_manual = False
        if tag == 'system' and manual == 1:
            system_manual = True
        return system_manual, data_manual


class SystemConfView(APIView):
    """
    系统配置
    get:
    查询系统配置
    post:
    更新网络配置， 请求体参数:
    tag, 更新类型 tag=dns, 更新dns，  tag=network， 更新端口配置
    tag=dns 时额外参数 [primary_dns: 首选DNS， secondary_dns: 备选DNS]，
    tag=network 时额外参数 [name: 网口名称, type: 网口用途, ipv4_addr: IPv4地址, ipv4_netmask: IPv4掩码,
                          ipv4_gateway: IPv4网关]
    """

    serializer_class = NetworkSerializer

    def get(self, request, *args, **kwargs):
        """系统配置
        网口配置  ports
        DNS配置  primary_dns secondary_dns
        运行模式配置  mode
        """
        djconsole_data = djconsole.get_network_list(request)
        data = {"status": "success", "errcode": 0, "data": djconsole_data}
        return Response(data=data)

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        exists_conf = djconsole.get_network_list(request)
        exists_conf = exists_conf.get("ports")
        if serializer.is_valid(raise_exception=True):
            request_data = serializer.validated_data
            tag = request_data.get("tag")
            if tag == "dns":
                update_conf = {}
                for conf in exists_conf:
                    if conf["type"] == "work":
                        update_conf = conf
                        break
                log_type = LogTypeName.system_dns.value
                update_conf.update(
                    {
                        "primary_dns": request_data.get("primary_dns"),
                        "secondary_dns": request_data.get("secondary_dns")
                        if request_data.get("secondary_dns")
                        else None,
                    }
                )
                detail = "修改dns配置为: 首选DNS{}，备选DNS{}，".format(
                    request_data.get("primary_dns"),
                    request_data.get("secondary_dns", ""),
                )
            else:
                log_type = LogTypeName.system_network.value
                name = request_data.get("name")
                update_conf = {}
                for conf in exists_conf:
                    if conf["name"] == name:
                        update_conf = conf
                update_conf.update(
                    {
                        "ipv4_addr": request_data.get("ipv4_addr"),
                        "ipv4_netmask": request_data.get("ipv4_netmask"),
                        "ipv4_gateway": request_data.get("ipv4_gateway"),

                        "ipv6_addr": request_data.get("ipv6_addr"),
                        "ipv6_netmask": request_data.get("ipv6_netmask"),
                        "ipv6_gateway": request_data.get("ipv6_gateway")
                    }
                )
                detail = "修改网口{}配置为:IPv4地址{},网关{},掩码{}, IPv6地址{},网关{},掩码{}".format(
                    update_conf["name"],
                    update_conf["ipv4_addr"],
                    update_conf["ipv4_gateway"],
                    update_conf["ipv4_netmask"],

                    update_conf["ipv6_addr"],
                    update_conf["ipv6_gateway"],
                    update_conf["ipv6_netmask"],
                )
            djconsole.update_network(request, config=update_conf)
            # 写日志
            log_data = {
                "module": LogModuleName.net_management.value,
                "action": AuditLogAction.up.value,
                "type": log_type,
                "desc": {"detail": detail},
            }
            audit_log(request=self.request, **log_data)
        result = {"status": "success", "errcode": 0, "message": "更新成功"}
        return Response(data=result)


class SupportView(APIView):
    """
    远程协助
    get:
    查询远程协助开关状态
    post:
    开始或者关闭远程协助 form参数 enabled  值是0或者1,
    需不需要开启web共享， form参数 web  值是0或者1
    """

    def get(self, request, *args, **kwargs):
        res = djconsole.get_support(request)
        data = {"status": "success", "errcode": 0, "data": res}
        return Response(data=data)

    def post(self, request, *args, **kwargs):
        enabled = request.data.get("enabled", "")
        # web = request.data.get("web", 0)
        web = 1
        enabled = self.check_value(enabled, "开启或者关闭远程协助参数只能是0或者1")
        web = self.check_value(web, "开启或者关闭远程协助web共享参数只能是0或者1")
        if enabled:
            res = djconsole.start_support(request, {"web": bool(web)})
        else:
            res = djconsole.close_support(request)
        data = {
            "status": "success",
            "errcode": 0,
            "message": "开启远程协助" if enabled else "关闭远程协助",
            "data": {"id": res.get("id")}
        }
        # 增加日志
        log_data = {
            "module": LogModuleName.system_management.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.remote_assistance.value,
            "desc": {"detail": data["message"]},
        }
        audit_log(request=self.request, **log_data)
        return Response(data)

    @staticmethod
    def check_value(value, message):
        if isinstance(value, str) and not value.isdigit():
            raise ValidationError(message)
        enabled = int(value)
        if enabled not in [0, 1]:
            raise ValidationError(message)
        return enabled


class RouteView(APIView):
    """
    路由接口
    """

    serializer_class = RouteSerializer

    def get(self, request, *args, **kwargs):
        routes = djconsole.get_route(request)
        data = {"status": "success", "errcode": 0, "data": routes}
        return Response(data=data)

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid(raise_exception=True):
            request_data = serializer.validated_data
            request_data['gateway'] = request_data.pop('via')
            request_data['ipaddress'] = request_data.pop('to')
            djconsole.add_route(request, request_data)
            data = {"status": "success", "errcode": 0, "message": "添加路由"}
            # 增加日志
            log_data = {
                "module": LogModuleName.net_management.value,
                "action": AuditLogAction.add.value,
                "type": LogTypeName.route.value,
                "desc": {"detail": data["message"]},
            }
            audit_log(request=self.request, **log_data)
            return Response(data)

    def delete(self, request):
        route_list = request.data.get('route_list', [])
        data = []
        for route in route_list:
            s = self.serializer_class(data=route)
            s.is_valid(raise_exception=True)

            request_data = s.validated_data
            request_data['gateway'] = request_data.pop('via')
            request_data['ipaddress'] = request_data.pop('to')
            data.append(request_data)

        djconsole.delete_route(request, {'route_list': data})
        data = {"status": "success", "errcode": 0, "message": "删除路由"}
        # 增加日志
        log_data = {
            "module": LogModuleName.net_management.value,
            "action": AuditLogAction.del_.value,
            "type": LogTypeName.route.value,
            "desc": {"detail": data["message"]},
        }
        audit_log(request=self.request, **log_data)
        return Response(data)


class ShutRebootView(APIView, MfaValidateMixin):
    """
    重启或者关闭设备
    post:
    重启或者关闭设备, 参数 action,  action=shutdown 关机， action=reboot 重启
    """

    def reboot(self):
        data = djconsole.reboot(self.request)
        return data

    def shutdown(self):
        data = djconsole.shutdown(self.request)
        return data

    def post(self, request, *args, **kwargs):
        action = request.data.get("action")
        if action not in ['shutdown', 'reboot']:
            raise ValidationError("只支持关机， 重启操作")
        # 增加日志
        desc = "关机" if action == "shutdown" else "重启"
        log_data = {
            "module": LogModuleName.system_management.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.turn_off_device.value if action == "shutdown" else LogTypeName.restart_device.value,
            "desc": {"detail": f"对设备 {request.data.get('node_ip', 'unknown')} 进行 {desc} 操作"},
        }
        # user = request.user
        # if user.mfa:
        #     data = {"func": action, "res": {}, "log_data": log_data}
        #     return self.mfa_before(request.user.username, 'shut_reboot', data)
        res = getattr(self, action)()
        audit_log(request=self.request, **log_data)
        data = {"status": "success", "errcode": 0, "message": res}
        return Response(data=data, status=200)


class PingView(APIView):
    """
    检测网络
    post:
    form参数 name 端口名称, 检测对应网口网络状态
    """

    def post(self, request, *args, **kwargs):
        name = request.data.get("name")
        if not name:
            raise ValidationError("请输入需要检测的网口名称")
        djconsole_data = djconsole.ping(request, name)
        data = {"status": "success", "errcode": 0, "data": djconsole_data}
        return Response(data=data)


class NetworkInfoView(APIView):
    """
    post:
    查网口数据信息, 请求体参数:
    time 查询趋势图时间范围只能是["day", "week"] time=day, 查询24小时数据， time=week查询一周数据，
    eth_range 查询网口范围，
    rate_type 查询数据类型 rate_type=bbs 接口速率, rate_type=pps 包转发率
    """

    def post(self, request, *args, **kwargs):
        time = self.request.data.get("time")
        if time not in ["day", "week"]:
            raise ValidationError("查询数据类型错误")
        # all_names = djconsole.get_network_name(request)  # 会有无效网口
        network = djconsole.get_network_list(request)
        all_names = [i['name'] for i in network.get('ports')]
        names = self.request.data.get("eth_range", [])
        kind = self.request.data.get("rate_type", "bps")
        if kind not in ["bps", "pps"]:
            raise ValidationError("查询数据类型错误")
        if not isinstance(names, list):
            raise ValidationError("查询数据类型错误")
        statistics_data = djconsole.get_network_statistics(request)
        detail_data = djconsole.get_network_detail(request, time, ",".join(names), kind)
        detail_data.update({"stat": statistics_data})
        result = {
            "status": "success",
            "errcode": 0,
            "data": detail_data,
            "all_eth": all_names,
        }
        return Response(data=result)


class StatusBarView(APIView):
    def get(self, request):
        network_status = djconsole.get_network_status(request)
        day, hour, minute = djconsole.get_uptime(request)
        license = djconsole.get_license_device(request)
        cpu_mem_status = djconsole.get_cpu_mem_status(request)
        data = {
            "status": "success",
            "errcode": 0,
            "data": {
                "network_status": network_status,
                "now": timezone.now().strftime("%Y-%m-%d %H:%M"),
                "running_time": f"{day}天{hour}小时{minute}分钟",
                "license_status": license.get("product_status"),
                "cpu_percent": cpu_mem_status.get("cpu_percent"),
                "memory_percent": cpu_mem_status.get("memory_percent"),
            },
        }
        return Response(data=data)


class SnmpView(APIView):
    """
    get:
    获取snmp相关信息，包括服务是否开启，snmp v1/v2c的团体名， v3的安全用户
    post:
    相关snmp操作，使用action字段控制
    action：参数为[off， on （控制开关服务），set（设置v1/v2团体名）, add(添加v3用户)，delete（删除v3用户）]
    """

    def get(self, request, *args, **kwargs):
        result = djconsole.get_snmp_info(request)
        return Response(data=result)

    def post(self, request, *args, **kwargs):
        action = self.request.data.get("action")
        group_name = self.request.data.get('group_name')
        user_info = self.request.data.get('user_info')
        if action not in ['off', 'on', 'set', 'add', 'delete']:
            raise ValidationError("没有该方法")
        if action in ['off', 'on']:
            result = djconsole.post_snmp_service(request, {"action": action})
            log_action = AuditLogAction.up.value
            detail = f"{'开启' if action == 'on' else '关闭'}snmp服务"
        elif action in ['set']:
            log_action = AuditLogAction.up.value
            if group_name:
                result = djconsole.set_snmp_name(request, {"group_name": group_name})
            else:
                raise ValidationError("请输入正确的值")
            detail = f"设置snmp v2 Community为 '{group_name}'"
        elif action in ['add']:
            log_action = AuditLogAction.create.value
            result = djconsole.add_snmp_user(request, user_info)
            detail = f"添加snmp v3用户 '{user_info}'"
        else:
            log_action = AuditLogAction.del_.value
            detail = f"删除snmp v3用户 '{user_info}'"
            result = djconsole.del_snmp_user(request, user_info)

        log_data = {
            "module": LogModuleName.net_management.value,
            "action": log_action,
            "type": LogTypeName.snmp.value,
            "desc": {"detail": detail},
        }
        audit_log(request=self.request, **log_data)
        return Response(data=result)


class SysNodeView(APIView):
    """集群"""

    def get(self, request, *args, **kwargs):
        is_cluster = djconsole.is_cluster(request)
        if is_cluster:
            detail = self.request.query_params.get("detail")
            detail = False if detail == 'false' else True
            data = {
                "status": "success",
                "errcode": 0,
                "data": djconsole.get_cluster_list(request, detail)
            }
            return Response(data=data)
        data = {
            "status": "success",
            "errcode": 0,
            "data": djconsole.get_device_status(request)
        }
        return Response(data=data)

    def post(self, request, *args, **kwargs):
        """
        post:
        集群模式添加节点, 请求体参数:
        node_ip 所添加节点的ip
        role 集群角色 暂定只能是["engine", "data", "dns"]中的一种
        node_name 节点名称
        """
        is_cluster = djconsole.is_cluster(request)
        if not is_cluster:
            return Response(data={
                "status": "fail",
                "errcode": -1,
                "data": {},
                "message": Tips().get(0)
            })
        role = self.request.data.get("role", '').strip()
        if role not in ["engine", "data", "dns"]:
            raise ValidationError(Tips().get(1))
        node_ip = self.request.data.get("node_ip", '').strip()
        node_name = self.request.data.get("node_name", '').strip()
        post_data = {"node_ip": node_ip, "node_name": node_name, "role": role}
        data = djconsole.add_node(request, post_data)
        result = {
            "status": "success",
            "errcode": 0,
            "data": data
        }
        return Response(data=result)


class NodeUptimeView(APIView):
    """节点开机时间"""

    def get(self, request, *args, **kwargs):
        day, hour, minute = djconsole.get_uptime(request)
        datas = {"status": "success", "errcode": 0, "data": f"{day}天{hour}小时{minute}分钟"}
        return Response(data=datas)


class NodeLastUpdateView(APIView):
    """节点最后更新时间"""

    def get(self, request, *args, **kwargs):
        update_info = djconsole.get_update_version(request)
        updated_at = update_info.get('updated_at')
        datas = {"status": "success", "errcode": 0, "data": updated_at}
        return Response(data=datas)


class ChangeNodeView(APIView):
    """
    post:
    集群模式操作节点, 请求体参数:
    device_id 节点证书id
    node_ip 节点的ip
    role 集群角色 暂定是["engine", "data", "dns", "mgmt"]
    node_name 节点名称
    type 节点操作类型 ['online','offline','delete','poweroff','reboot']
    """

    def post(self, request, *args, **kwargs):
        is_cluster = djconsole.is_cluster(request)
        if not is_cluster:
            return Response(data={
                "status": "fail",
                "errcode": -1,
                "data": {},
                "message": "当前服务器不为集群模式"
            })
        device_id = self.request.data.get("device_id", '').strip()
        if not device_id:
            raise ValidationError("节点id不能为空")

        node_id = self.request.data.get("id", '').strip()
        node_ip = self.request.data.get("node_ip", '').strip()
        node_name = self.request.data.get("node_name", '').strip()
        role = self.request.data.get("role", '').strip()
        operate_type = self.request.data.get("type", '').strip()
        if role == 'mgmt' and operate_type == 'delete':
            raise ValidationError("管理节点无法删除")
        if role not in ["engine", "data", "dns", "mgmt"]:
            raise ValidationError("未知的节点类型")
        if operate_type not in ['online', 'offline', 'delete', 'poweroff', 'reboot']:
            raise ValidationError("操作类型错误")

        post_data = {"id": node_id, "node_ip": node_ip, "node_name": node_name, "role": role, "device_id": device_id}
        data = djconsole.change_node(request, post_data, operate_type)
        result = {
            "status": "success",
            "errcode": 0,
            "data": data
        }
        return Response(data=result)


class UpdateLogView(APIView):
    """
    设备升级日志
    get:
    升级日志
    """

    def get(self, request):
        data = djconsole.update_log(request)
        return Response(data=data)


class BackupView(APIView):
    """
    数据备份
    """
    def post(self, request):
        data = djconsole.bk_backup(self.request)
        result = {
            "status": "success",
            "errcode": 0,
            "message": data.get('detail')
        }
        log_data = {
            "module": LogModuleName.backup.value,
            "action": AuditLogAction.add.value,
            "type": LogTypeName.backup.value,
            "desc": {"detail": "备份数据文件"},
        }
        audit_log(request=self.request, **log_data)
        return Response(data=result)


class RecoveryView(APIView):
    """
    数据恢复
    """
    def post(self, request):
        kb_id = self.request.data.get("id", "")
        data = djconsole.bk_recovery(self.request, kb_id)
        result = {
            "status": "success",
            "errcode": 0,
            "message": data.get('detail')
        }
        log_data = {
            "module": LogModuleName.backup.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.backup.value,
            "desc": {"detail": f"恢复数据{kb_id}"},
        }
        audit_log(request=self.request, **log_data)
        return Response(data=result)


class ProgressView(APIView):
    """
    查询备份恢复进度进度
    """
    permission_classes = [AllowAny, ]

    def get(self, request):
        kind = self.request.query_params.get("kind", "")
        if kind not in ["backup", "recovery"]:
            return Response(
                data={"status": "fail", "errcode": -1, "message": "参数错误"}
            )
        data = djconsole.bk_progress(self.request, kind)
        message = data.pop("message", "")
        result = {
            "status": "success",
            "errcode": 0,
            "data": data,
            "message": message
        }
        return Response(data=result)


class BkRecoveryRecord(APIView):
    """
    备份恢复记录
    get:
       查询备份与恢复记录
    post:
        删除备份文件
    """

    def get(self, request):
        kind = self.request.query_params.get("kind", "")
        if kind not in ["backup", "recovery"]:
            return Response(
                data={"status": "fail", "errcode": -1, "message": "参数错误"}
            )
        data = djconsole.bk_query_record(self.request, kind)
        result = {
            "status": "success",
            "errcode": 0,
            "data": data,
        }
        return Response(data=result)

    def post(self, request):
        kb_ids = self.request.data.get("ids", [])
        if (not isinstance(kb_ids, list)) or not kb_ids:
            return Response(
                data={"status": "fail", "errcode": -1, "message": "参数错误"}
            )
        data = djconsole.bk_delete_record(self.request, kb_ids)
        result = {
            "status": "success",
            "errcode": 0,
            "message": data.get("detail", ""),
        }
        log_data = {
            "module": LogModuleName.backup.value,
            "action": AuditLogAction.del_.value,
            "type": LogTypeName.backup.value,
            "desc": {"detail": f"删除备份{'、'.join(kb_ids)}"},
        }
        audit_log(request=self.request, **log_data)
        return Response(data=result)


class BkRecoveryFile(APIView):
    """
    下载、上传备份文件
    get:
         下载备份文件
    post:
         设置上传文件
    """

    def get(self, request):
        file_id = self.request.query_params.get("id", "")
        if not file_id:
            return Response(
                data={"status": "fail", "errcode": -1, "message": "下载失败, 请选择需要下载的备份文件"},
                status=400
            )
        resp = djconsole.bk_download_file(request, bk_id=file_id)
        log_data = {
            "module": LogModuleName.backup.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.backup.value,
            "desc": {"detail": f"下载备份文件 {file_id}"},
        }
        audit_log(request=self.request, **log_data)
        if resp.status_code == 200:
            from _io import BytesIO
            response = FileResponse(BytesIO(resp.content))
            response['Content-Disposition'] = resp.headers.get("Content-Disposition")
            return response
        else:
            return Response(
                data={"status": "fail", "errcode": -1, "message": "下载失败"},
                status=400
            )

    def post(self, request):
        conf = self.request.data
        data = djconsole.bk_upload_file(request, conf=conf)
        mg = data.get("detail", "")
        is_success = mg == "上传成功"
        result = {
            "status": "success" if is_success else "fail",
            "errcode": 0 if is_success else -1,
            "message": "正在加载文件，请稍后在备份列表进行恢复" if is_success else "操作失败，请检测配置",
        }
        log_data = {
            "module": LogModuleName.backup.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.backup.value,
            "desc": {"detail": f"上传备份文件， sha1: {conf.get('sha1', '')}"},
        }
        audit_log(request=self.request, **log_data)
        return Response(data=result)


class SshView(APIView):

    def get(self, request):
        data = djconsole.get_ssh_status(request=request)
        return Response(
            data={"status": "success", "errcode": 0, "message": "", "data": data},
            status=200
        )

    def post(self, request):
        action = self.request.data.get("action")
        if action not in ["on", "off"]:
            return Response(
                data={"status": "fail", "errcode": -1, "message": "不支持的操作"},
                status=400
            )
        data = djconsole.change_ssh_status(request, action)
        log_data = {
            "module": LogModuleName.net_management.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.ssh.value,
            "desc": {"detail": f"{'开启' if action == 'on' else '关闭' } ssh 登录端口"},
        }
        audit_log(request=self.request, **log_data)
        return Response(
            data={"status": "success", "errcode": 0, "message": data.get("detail")},
            status=200
        )
